
#ifndef __BLOCK_WRITE_CLIENT_H
#define __BLOCK_WRITE_CLIENT_H

#include <deque>
#include "BlockWriteServer.h"
#include "UDPSend.h"

/** A structure describing a block for upload. */
typedef struct blockforupload
{
   /** Actor ID who is the sender of the block. */
   char *sender;
   /** Actor ID who is the recipient of the block. */
   char *recipient;
   /** Host name to upload the block to. */
   char *udp_hostname;
   /** Port number to upload the block to. */
   int udp_portno;
   /** Host name for the control connection. */
   char *tcp_hostname;
   /** Port number for the control connection. */
   int tcp_portno;
   /** Data for this active block. */
   uint8_t *active_block_data;
   /** The unencrypted hash is a readable hexadecimal hash of the
    * plaintext unencrypted block. This is necessary in the case that
    * the uploaded data is okay and has been previously verified to be
    * okay, but re-encryption of the block has changed all the hashing
    * blocks hashes making something that is complete and usable on
    * disk completely invalid and need to be rewritten. To solve this
    * we use the unencrypted hash which is appended to the temporary
    * block file after everything else. If this hash exists and
    * matches, we know that even if all hashes fail, this is the same
    * usable data block. */
   char *unencrypted_hash;
   /** This will be NULL until someone (a child thread to make it
    * non-blocking) decides to populate it. It can be put back on the
    * queue once it has been calculated and made non-NULL. */
   uint8_t *active_block_hashes;
   /** Suggested file name. */
   char *filename;
   /** Remote offset of block. */
   apr_off_t offset;
   /** Total file length. */
   apr_size_t filelength;
   /** Total block length (of this local block) */
   apr_size_t blocklength;
} BlockForUpload;

/** A struct shared between threads, a collection of pointers to datagrams which
 * allow communication between the each of the client threads and a block on the
 * server side. The pointers on the pipes are NULL and inactive until a block is
 * asked for. */
typedef struct serverpipedatagrams
{
   /** Actor ID who is the sender of the block. */
   char *sender;
   /** Actor ID who is the recipient of the block. */
   char *recipient;
   /** The memory pool local to this thread. */
   apr_pool_t *localmp;
   /** Is this server pipe active? */
   uint32_t active;
   /** Is this server pipe connected to a remote block? */
   uint16_t blockID;
   /** Does the server say it is complete? */
   uint32_t server_says_complete;
   /** Bit vector of complete blocks. */
   uint32_t filled_bit_vector[(ACTIVE_BLOCK_SIZE / PACKET_BLOCK_SIZE) / (sizeof(uint32_t) * 8)];
   /** Data to send. */
   void *active_block_data;
   /** Hostname/address to send UDP to. */
   char *udp_hostname;
   /** Port number to send UDP to. */
   int udp_portno;
   /** Hostname/address to send TCP to. */
   char *tcp_hostname;
   /** Port number to send TCP to. */
   int tcp_portno;
   /** The UDPSend class for this thread. */
   UDPSend *udp_sender;
   /** Mutex for starting sessions. */
   APRMutex *sessionstart;
} ServerPipeDatagrams;

class BlockWriteClient
{
   public:
      BlockWriteClient(int inoofthreads, KeyDB *itkeydb, apr_pool_t *upperpool);
      ~BlockWriteClient();
      void handleBlocksOnThread(int threadid, apr_pool_t *localmp);
      static void *handleBlocksOnThreadStatic(apr_thread_t *tthread, void *args);
      /** Get the block handler as a function pointer. */
      virtual functor getHandleBlocksFunction() const { return (functor)(&handleBlocksOnThreadStatic); }
      void watchFromThread(apr_pool_t *localmp);
      static void *watchFromThreadStatic(apr_thread_t *tthread, void *args);
      /** Get the watch function as a function pointer. */
      virtual functor getWatchFromThreadFunction() const { return (functor)(&watchFromThreadStatic); }
   protected:
      int queueBlock(const char *hostname, int portno,
         const char *suggested_filename, apr_off_t offset, apr_size_t filesize, uint8_t *data_pointer,
         apr_size_t blocklength, const char *unencrypted_hash);
      int requestUploadSlot(int threadid, apr_pool_t *localmp);
      int hashThreadLocalBlockAndRequestUploadSlot(int threadid, apr_pool_t *localmp);
      /** The main key database. */
      KeyDB *tkeydb;
      /** Array of actively uploading blocks to active server slots. */
      ServerPipeDatagrams *uploadarray;
      /** The many per-activeblock threads. */
      apr_thread_t **blocks_threads;
      /** The many thread attributes for the many active block threads. */
      apr_threadattr_t **blocks_threadattrs;
      /** The watch thread. */
      apr_thread_t *watch_thread;
      /** The thread attributes for the watch thread. */
      apr_threadattr_t *watch_threadattr;
      /** Flag to stop the blocks threads. */
      int allblocksstop;
      /** Flag to stop the watch thread. */
      int watchstop;
      /** Active uploading slots. */
      uint32_t activeslots;
      /** The number of client threads. */
      int noofthreads;
      /** The memory pool. */
      apr_pool_t *mp;
      /** Mutex and wait for the block queue. */
      APRMutex *queue_mutex;
      /** Block queue. */
      std::deque<BlockForUpload *> uploadqueue;
};

/*
BlockWriteClient
----------------

Have a controlling thread that allocates new outgoing blocks to child thread.
Has many child threads to send the sets of block data.
Needs a connection between Broker5Server and the BlockWriteServer.
Probably give the Broker5Server a pointer to BlockWriteServer.
Then do:

Clnt queries BlockWriteClient for open thread slot:
if successful generate a new block.
then:

B5Clnt does:
TRANSFER_START_BLOCK
transfername: %s
activeblockindex: %d
activeblockoffset: %d
activeblocklength: %d
activeblockSHA256: %08X%08X...
blockSHA1list:
%08X%08X%08X...
%08X%08X%08X...
...

B5Srvr does:
if transfername exists then delete it.
blocks out 0xFF for two files - force actual allocation
one is used, one is standby
then:
TRANSFER_START_AUTHRSD
slotID: %d

or

TRANSFER_START_REFUSED
with error messages like
TRANSFER_ALLOC_FAILED

then

throw packets, when complete...
so can call Broker5Client

Broker5Server has subservient pointer BlockWriteServer
BlockWriteClient has subservient point Broker5Client

TRANSFER_PROGRESS_VECTOR
to get bit vector from server (dirty read)
write bit vector to BlockWriteClient this thread active block
re-upload packets that didn't succeed.

TRANSFER_COMPLETE
*/

#endif
